package com.ringtheweb.serviceprovider.weather.core;

import static com.ringtheweb.serviceprovider.weather.core.LocationTreeDiscoveringEvents.*;

import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;

import com.ringtheweb.event.Events;
import com.ringtheweb.serviceprovider.weather.model.Location;
import com.ringtheweb.serviceprovider.weather.model.LocationType;
import com.ringtheweb.serviceprovider.weather.model.Transition;

@Component("locationTree")
public class LocationTree {
	
	private Location rootLocation = null;
	
	private ILocationRetriever[] locationRetrievers;
	
    private static Log LOG = LogFactory.getLog(LocationTree.class);
	
	public LocationTree() {
		locationRetrievers = new ILocationRetriever[]{ new WorldToWorldRegionRetriever(),
				new WorldRegionToStateRetriever(), new StateToCityRetriever()};
		
		try {
			rootLocation = new Location("world",
					LocationType.values()[0],new URL("http://weather.yahoo.com"));
		}
		catch(MalformedURLException e) {
			LOG.error(String.format("Url not well formed: %s","http://weather.yahoo.com"), e);
		}
		
	}
	
	
	public void walk() {
		walk(rootLocation);
	}


	public Location getRootLocation() {
		return rootLocation;
	}


	public void setRootLocation(Location rootLocation) {
		this.rootLocation = rootLocation;
	}
	
	
	
	public ILocationRetriever findLocationRetriever(Location location) {

		Transition<LocationType> locationParentToChildrenTransition;
		LocationType toType = null;
		try {
			toType = LocationType.values()[location.getLevel()+1]; 
		}
		catch(IndexOutOfBoundsException e ) {
			
		}
		
		locationParentToChildrenTransition = new Transition<LocationType>(location.getLocationType(),toType);
		
		for (ILocationRetriever locationRetriever : locationRetrievers ) {
			if (locationRetriever.getTransition().equals(locationParentToChildrenTransition)) {
				return locationRetriever;
			}
		}
		
		return null;
	}

	
    /**
     * Walks the Tree in pre-order style. This is a recursive method, and is
     * called from the toList() method with the root element as the first
     * argument. It appends to the second argument, which is passed by reference     * as it recurses down the tree.
     * @param element the starting element.
     * @param list the output of the walk.
     */
    private void walk(Location location) {
    	
    	if (location.isCompleted()) return;
    	
    	Events.instance().raiseEvent(BEFORE_CURRENT_RETRIEVAL.toString(),location);
    	if (location.getChildren() == null || location.getChildren().size() == 0) {
    		ILocationRetriever locationRetriever = findLocationRetriever(location);
    		if (locationRetriever == null) return;
    		LOG.info(String.format("Retrieving child locations for: %s", location));
    		locationRetriever.retrieveChildren(location);
    	}
    	Events.instance().raiseEvent(AFTER_CURRENT_RETRIEVAL.toString(),location);
        for (Location childLocation : location.getChildren()) {
        	//Make sure it's not already completed before walking
            walk(childLocation);
            childLocation.setCompleted(true);
           Events.instance().raiseEvent(AFTER_NEXT_CHILD_RETRIEVAL.toString(),location,childLocation);
        }
        location.setCompleted(true);
        Events.instance().raiseEvent(AFTER_LAST_CHILD_RETRIEVAL.toString(),location);
    }

}
